#pragma once

#include "MBDataTypes.h"
#include "MBMatrix3x3.h"
#include "MBMatrix4x4.h"

#include <vector>

/**
  * An MBCameraModelIntrinsicParameters object encapsulates the intrinsic
  * parameters of a camera model. These are the resolution of a taken photo in
  * pixels (width, height) and the dimensions of a single pixel in meters.
  */
class MBCameraModelIntrinsicParameters
{
public:
	/**
	  *	Constructs a new MBCameraModelIntrinsicParameters object.
	  *
	  * @param pixelWidthInMeters width of a single pixel in meters
	  * @param pixelHeightInMeters height of a single pixel in meters
	  * @param imageWidthInPixels width of a taken photo in pixels
	  * @param imageHeightInPixels height of a taken photo in pixels
	  */
	MBCameraModelIntrinsicParameters(	const double &pixelWidthInMeters,
										const double &pixelHeightInMeters,
										const MBuint &imageWidthInPixels,
										const MBuint &imageHeightInPixels ) :
		mPixelWidthInMeters( pixelWidthInMeters ),
		mPixelHeightInMeters( pixelHeightInMeters ),
		mImageWidthInPixels( imageWidthInPixels ),
		mImageHeightInPixels( imageHeightInPixels ) {};

	/**
	  *	Returns true when the current MBCameraModelIntrinsicParameters object
	  * is equal to the passed one.
	  *
	  * @param other const reference to the object, that is compared to the current one
	  * @return true when the current MBCameraModelIntrinsicParameters object is equal to the passed one, false otherwise
	  */
	bool operator==( const MBCameraModelIntrinsicParameters &other ) const
	{
		return (mPixelWidthInMeters == other.mPixelWidthInMeters &&
			mPixelHeightInMeters == other.mPixelHeightInMeters &&
			mImageWidthInPixels == other.mImageWidthInPixels &&
			mImageHeightInPixels == other.mImageHeightInPixels);
	}

	/** width of a single pixel in meters */
	double mPixelWidthInMeters;
	/** height of a single pixel in meters */
	double mPixelHeightInMeters;
	/** width of a taken photo in pixels */
	MBuint mImageWidthInPixels;
	/** height of a taken photo in pixels */
	MBuint mImageHeightInPixels;
};

/**
  *	A MBCameraModelInternalCalibrationParameters object encapsulates the
  * internal calibration parameters of a camera model. These are the focal
  * lengths in pixels in X- and Y-direction, the center of a taken image in
  * pixels in X- and Y-direction, four parameters (K1 - K4) for the radial
  * distortion and two parameters (P1, P2) for the tangential distortion. The
  * distortion parameters are assumed to be the same as the ones in a
  * RiScan Pro (RSP) project
  * (http://www.riegl.com/products/software-packages/riscan-pro/).
  * Refer to the *.dtd-file of an RSP-project for more information.
  */
class MBCameraModelInternalCalibrationParameters
{
public:
	/**
	  * Constructs a new MBCameraModelInternalCalibrationParameters object.
	  *
	  * @param focalLengthXInPixels focal length of the camera in X-direction in pixels
	  * @param focalLengthYInPixels focal length of the camera in Y-direction in pixels
	  * @param centerOfImageXInPixels center of a taken image in pixels in X-direction
	  * @param centerOfImageYInPixels center of a taken image in pixels in Y-direction
	  * @param radialDistortionParamK1 radial distortion parameter K1
	  * @param radialDistortionParamK2 radial distortion parameter K2
	  * @param radialDistortionParamK3 radial distortion parameter K3
	  * @param radialDistortionParamK4 radial distortion parameter K4
	  * @param tangentialDistortionParamP1 tangential distortion parameter P1
	  * @param tangentialDistortionParamP2 tangential distortion parameter P2
	  * @param differentialScale differential scale factor
	  * @param nonOrthogonality nonorthogonality factor
	  */
	MBCameraModelInternalCalibrationParameters(
								const double &focalLengthXInPixels,
								const double &focalLengthYInPixels,
								const double &centerOfImageXInPixels,
								const double &centerOfImageYInPixels,
								const double &radialDistortionParamK1 = 0.0,
								const double &radialDistortionParamK2 = 0.0,
								const double &radialDistortionParamK3 = 0.0,
								const double &radialDistortionParamK4 = 0.0,
								const double &tangentialDistortionParamP1 = 0.0,
								const double &tangentialDistortionParamP2 = 0.0,
								const double &differentialScale = 0.0,
								const double &nonOrthogonality = 0.0 ) :
		mFocalLengthXInPixels( focalLengthXInPixels ),
		mFocalLengthYInPixels( focalLengthYInPixels ),
		mCenterOfImageXInPixels( centerOfImageXInPixels ),
		mCenterOfImageYInPixels( centerOfImageYInPixels ),
		mRadialDistortionParamK1( radialDistortionParamK1 ),
		mRadialDistortionParamK2( radialDistortionParamK2 ),
		mRadialDistortionParamK3( radialDistortionParamK3 ),
		mRadialDistortionParamK4( radialDistortionParamK4 ),
		mTangentialDistortionParamP1( tangentialDistortionParamP1 ),
		mTangentialDistortionParamP2( tangentialDistortionParamP2 ),
		mDifferentialScale( differentialScale ),
		mNonOrthogonality( nonOrthogonality ) {};

	/**
	  *	Returns true when the current MBCameraModelInternalCalibrationParameters object
	  * is equal to the passed one.
	  *
	  * @param other const reference to the object, that is compared to the current one
	  * @return true when the current MBCameraModelInternalCalibrationParameters object is equal to the passed one, false otherwise
	  */
	bool operator==( const MBCameraModelInternalCalibrationParameters &other ) const
	{
		return (mFocalLengthXInPixels == other.mFocalLengthXInPixels &&
			mFocalLengthYInPixels == other.mFocalLengthYInPixels &&
			mCenterOfImageXInPixels == other.mCenterOfImageXInPixels &&
			mCenterOfImageYInPixels == other.mCenterOfImageYInPixels &&
			mRadialDistortionParamK1 == other.mRadialDistortionParamK1 &&
			mRadialDistortionParamK2 == other.mRadialDistortionParamK2 &&
			mRadialDistortionParamK3 == other.mRadialDistortionParamK3 &&
			mRadialDistortionParamK4 == other.mRadialDistortionParamK4 &&
			mTangentialDistortionParamP1 == other.mTangentialDistortionParamP1 &&
			mTangentialDistortionParamP2 == other.mTangentialDistortionParamP2 &&
			mDifferentialScale == other.mDifferentialScale &&
			mNonOrthogonality == other.mNonOrthogonality);
	}

	/** focal length of the camera in X-direction in pixels */
	double mFocalLengthXInPixels;
	/** focal length of the camera in Y-direction pixels */
	double mFocalLengthYInPixels;
	/** center of a taken image in pixels in X-direction */
	double mCenterOfImageXInPixels;
	/** center of a taken image in pixels in Y-direction */
	double mCenterOfImageYInPixels;
	/** radial distortion parameter K1 */
	double mRadialDistortionParamK1;
	/** radial distortion parameter K2 */
	double mRadialDistortionParamK2;
	/** radial distortion parameter K3 */
	double mRadialDistortionParamK3;
	/** radial distortion parameter K4 */
	double mRadialDistortionParamK4;
	/** tangential distortion parameter P1 */
	double mTangentialDistortionParamP1;
	/** tangential distortion parameter P2 */
	double mTangentialDistortionParamP2;
	/** differential scale factor */
	double mDifferentialScale;
	/** non orthogonality factor */
	double mNonOrthogonality;
};

/**
  *	An MBCameraModel object encapsulates all the data of a particular
  * camera model to make a perspective projection onto the image plane of
  * the camera possible. Therefore the intrinsic and internal calibration
  * parameters are needed.
  */
class MBCameraModel
{
public:
	MBCameraModel(
		const MBCameraModelIntrinsicParameters &intrinsicParams,
		const MBCameraModelInternalCalibrationParameters &internalCalibrationParams,
		const bool &imageOriginTopLeft,
		const MBuint rieglVersion = 0 );

	const bool &IsImageOriginTopLeft( void ) const;
	const MBMatrix3x3 &GetCameraMatrix( void ) const;
	const double &GetDifferentialScale( void ) const;
	const std::vector<double> &GetDistortionCoefs( void ) const;
	const MBCameraModelInternalCalibrationParameters &GetInternalCalibrationParameters( void ) const;
	const MBCameraModelIntrinsicParameters &GetIntrinsicParameters( void ) const;
	const double &GetNonOrthogonality( void ) const;
	const MBuint &GetRieglVersion( void ) const;
	
	bool operator==( const MBCameraModel &other ) const;
private:
	/** intrinsic parameters of the camera model */
	MBCameraModelIntrinsicParameters mIntrinsicParams;
	/** internal calibration parameters */
	MBCameraModelInternalCalibrationParameters mInternalCalibrationParams;
	/** 3x3 camera matrix, that is needed for the perspective projection.
	  *
	  *					 | fx  0  cx |
	  *  mCameraMatrix = | 0  fy  cy |
	  *					 | 0   0   1 |
	  */
	MBMatrix3x3 mCameraMatrix;
	/** vector containing the distortion coefficients in the order K1, K2, P1, P2, K3, K4 */
	std::vector<double> mDistortionCoefs;
	/** True when the the image origin is in the top left corner, false otherwise */
	bool mIsImageOriginTopLeft;
	/** non orthogonality factor */
	double mNonOrthogonality;
	/** differential scale factor */
	double mDifferentialScale;
	/** Only needed for RiScan Pro projects. For software versions < 1.5.0 mVersion is 0, otherwise it is 1 */
	MBuint mRieglVersion;
};